package com.enen.interceptor;

import com.enen.utils.CurrentHolder;
import com.enen.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * TokenInterceptor 是一个自定义的拦截器，用于对 HTTP 请求进行拦截处理。
 * 该拦截器主要用于处理与令牌（Token）相关的业务逻辑，例如令牌验证等。
 * 它实现了 Spring 框架的 HandlerInterceptor 接口，并重写了其中的三个方法，
 * 分别在请求处理的不同阶段进行相应的操作。
 */
@Slf4j
@Component // 将当前类交给 Spring 管理，使其成为 Spring 容器中的一个组件
public class TokenInterceptor implements HandlerInterceptor {

    /**
     * 该方法在请求处理之前被调用，是拦截器的核心方法之一。
     * 通常用于进行一些预处理操作，如身份验证、权限检查、请求日志记录等。
     * 如果该方法返回 true，则请求会继续向下传递到控制器进行处理；
     * 如果返回 false，则请求将被拦截，不会继续向下传递。
     *
     * @param request  当前的 HTTP 请求对象，包含了请求的所有信息，如请求头、请求参数等。
     * @param response 当前的 HTTP 响应对象，用于向客户端发送响应信息。
     * @param handler  执行链中的下一个处理器，通常是控制器方法。
     * @return 如果返回 true，则请求继续处理；如果返回 false，则请求被拦截。
     * @throws Exception 可能抛出的异常，在处理过程中如果出现异常会抛出。
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1. 获取当前请求的 URI（统一资源标识符），它标识了客户端请求的具体资源路径
        String uri = request.getRequestURI();

        //(因为在配置类中设置了login不拦截，所以这里代码注释掉，可不写)
        // 2. 检查请求的 URI 是否包含 "/login"，若包含则认为是登录请求，直接放行该请求
        // 因为登录请求不需要进行 token 验证，允许用户正常访问登录接口
//        if (uri.contains("/login")) {
//            // 记录日志，表明登录请求已成功通过拦截器
//            log.info("登录请求，直接放行");
//            // 返回 true 表示允许该请求继续处理，后续代码将不再执行
//            return true;
//        }

        // 3. 从请求头中获取名为 "token" 的字段值，该值通常是客户端在登录成功后获得的 JWT 令牌
        String token = request.getHeader("token");

        // 4. 检查获取到的 token 是否为 null 或者为空字符串
        // 如果 token 不存在，说明用户未携带有效的令牌进行请求，可能未登录
        if (token == null || token.isEmpty()) {
            // 设置响应的状态码为 401，表示未授权，即用户未登录
            response.setStatus(401);
            // 返回 false 表示拦截该请求，不再继续处理
            return false;
        }

        // 5. 验证 token 的有效性，尝试解析该 token
        try {
            // 调用 JwtUtils 工具类的 parseToken 方法对 token 进行解析
            // 如果解析成功，说明 token 是有效的
            Claims claims = JwtUtils.parseToken(token);
            Integer empId = Integer.valueOf(claims.get("id").toString());
            CurrentHolder.setCurrentId(empId);
            log.info("token解析成功, 放行");

        } catch (Exception e) {
            // 如果解析过程中抛出异常，说明 token 无效，可能是被篡改或者已过期
            // 设置响应的状态码为 401，表示未授权，即用户未登录
            response.setStatus(401);
            // 返回 false 表示拦截该请求，不再继续处理
            return false;
        }

        // 6. 如果以上检查都通过，说明用户已登录且 token 有效，放行该请求
        log.info("用户已登录，token 验证通过，请求放行");
        // 返回 true 表示允许该请求继续处理
        return true;
    }

    /**
     * 该方法在请求处理之后、视图渲染之前被调用。
     * 可以在这个方法中对请求处理的结果进行一些后处理操作，
     * 例如修改 ModelAndView 中的数据、添加额外的响应头信息等。
     *
     * @param request      当前的 HTTP 请求对象。
     * @param response     当前的 HTTP 响应对象。
     * @param handler      执行链中的下一个处理器。
     * @param modelAndView 包含了控制器处理结果的 ModelAndView 对象，
     *                     可以通过它修改模型数据或视图名称。
     * @throws Exception 可能抛出的异常。
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        //7. 清空当前线程绑定的id
        CurrentHolder.remove();
    }

    /**
     * 该方法在整个请求处理完毕后被调用，无论请求处理过程中是否发生异常。
     * 通常用于进行一些资源清理操作，如关闭数据库连接、释放缓存等。
     *
     * @param request  当前的 HTTP 请求对象。
     * @param response 当前的 HTTP 响应对象。
     * @param handler  执行链中的下一个处理器。
     * @param ex       请求处理过程中抛出的异常，如果没有异常则为 null。
     * @throws Exception 可能抛出的异常。
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}